package com.meshnetics.gb.type.impl.zcl;

import com.meshnetics.gb.type.DataTypeID;
import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.MandatoryFormatter;
import com.meshnetics.gb.type.impl.ByteUtils;

import java.nio.ByteBuffer;

/**
 * WRITE DESCRIPTION HERE
 *
 * @author AKovylin
 */
public class TypeBool extends AbstractDataType {
    public static final TypeBool BOOLEAN = new TypeBool();

    protected static final byte FALSE = 0x00;
    protected static final byte TRUE = 0x01;
    protected static final byte INVALID = (byte) 0xff;

    private static final DataTypeID LOGICAL_DATA_TYPE_ID = new DataTypeID((byte) 0x10, "Logical", "Boolean");

    private final LogicalDataTypeMandatoryFormatter formatter;

    protected final Bool dataValueFalse = new Bool(this, FALSE);
    protected final Bool dataValueTrue = new Bool(this, TRUE);
    protected final Bool dataValueInvalid = new Bool(this, INVALID);

    protected TypeBool() {
        super(new String[]{"boolean", "Boolean", "bool"}, LOGICAL_DATA_TYPE_ID);
        this.formatter = new LogicalDataTypeMandatoryFormatter(this);
    }

    public DataValue fromObject(Object value) {
        DataValue res = null;
        if (value == null) {
            res = dataValueInvalid;
        } else if (!(value instanceof Boolean)) {
            throw new IllegalArgumentException("value must be Boolean");
        }
        if (res == null) {
            res = value == Boolean.FALSE ? dataValueFalse : dataValueTrue;
        }
        return res;
    }

    public DataValue valueOf(byte[] value) {
        if (value == null) {
            throw new NullPointerException("value is null");
        }
        if (value.length != 1) {
            throw new IllegalArgumentException("value must be 1 byte length");
        }
        return valueOf(value[0]);
    }

    public DataValue deserialize(ByteBuffer bb) {
        ByteUtils.ensureNotNull(bb);
        ByteUtils.ensureCapacity(bb, 1);
        byte b = bb.get();
        return valueOf(b);
    }

    private DataValue valueOf(byte b) {
        DataValue res;
        switch (b) {
            case FALSE:
                res = dataValueFalse;
                break;
            case TRUE:
                res = dataValueTrue;
                break;
            case INVALID:
                res = dataValueInvalid;
                break;
            default:
                throw new IllegalArgumentException("not a boolean");
        }
        return res;
    }

    public MandatoryFormatter getMandatoryFormatter() {
        return formatter;
    }

    protected static class LogicalDataTypeMandatoryFormatter extends AbstractMandatoryFormatter {
        private static final String S_TRUE = "01";
        private static final String S_FALSE = "00";
        private static final String S_INVALID = "ff";

        public LogicalDataTypeMandatoryFormatter(TypeBool dataType) {
            super(dataType);
        }

        public DataValue valueOf(String serializedValue) {
            if (serializedValue == null) {
                throw new NullPointerException("serializedValue is null");
            }
            DataValue res;
            if (S_TRUE.equals(serializedValue)) {
                res = ((TypeBool) dataType).dataValueTrue;
            } else if (S_FALSE.equals(serializedValue)) {
                res = ((TypeBool) dataType).dataValueFalse;
            } else if (S_INVALID.equals(serializedValue)) {
                res = ((TypeBool) dataType).dataValueInvalid;
            } else {
                throw new IllegalArgumentException("not a boolean");
            }
            return res;
        }

        public String formatSerializedString(DataValue value) {
            checkValue(value);
            String res;
            switch (((Bool) value).value) {
                case FALSE:
                    res = S_FALSE;
                    break;
                case TRUE:
                    res = S_TRUE;
                    break;
                case INVALID:
                    res = S_INVALID;
                    break;
                default:
                    throw new IllegalArgumentException("not a boolean");
            }
            return res;
        }
    }
}
